BoxLang 🚀 A New JVM Dynamic Language Learn More...

raygun4cfml

v3.0.0 Logging

Raygun4CFML

CFML client library for Raygun Crash Reporting.

Current Version: 3.0.0

Supported Platforms:

  • Adobe ColdFusion 2021+
  • Lucee 5.3+
  • BoxLang 1+

Active Development

3.0.0 adds breadcrumbs, onBeforeSend hooks, ignore exceptions, wildcard content filtering, payload size enforcement, configurable API endpoint/timeout, automatic retry, and additional environment fields — plus numerous bug fixes and 174 test specs across 20 engines.

Please be aware that no testing and work has yet gone into framework-specific crash reports, e.g. a deeper integration with Coldbox HMVC, Fusebox, CF on Wheels etc. This will be added over time in future releases.

Installation

Using CommandBox (Preferred Method)

  1. Install via CommandBox:

    To install the latest version from the master repository, use:

    box install raygun4cfml
    

    To install a specific release or tag, use:

    box install git://github.com/MindscapeHQ/raygun4cfml.git#{tagname}
    

    Alternatively, you can use:

    box install MindscapeHQ/raygun4cfml#{tagname}
    
  2. Setup:

    After installation, follow the setup instructions in the 'Library Usage' section below.

Manual Installation

  1. Clone or Download:

    • Fork and clone the repository to your local system, or download a zip file of the current content or a specific release/tag.
  2. Move Files:

    • Move the src and/or tests directories to locations suitable for your system.
  3. Dependencies:

    • Note that manual installation will not automatically resolve dependencies.

Quick Start

raygun = new com.raygun.RaygunClient(apiKey = "YOUR_API_KEY");

try {
    // your application code
    result = 14 / 0;
} catch (any e) {
    raygun.send(e);
}

Place the contents of /src in your webroot, or create a mapping to /com in your server administrator or through code.

Library Usage

RaygunClient

The RaygunClient is the primary component for sending error reports to Raygun.

init()

raygun = new com.raygun.RaygunClient(
    apiKey           = "YOUR_API_KEY",
    contentFilter    = contentFilterInstance,  // optional RaygunContentFilter
    appVersion       = "1.2.3",               // optional application version string
    settings         = settingsInstance,       // optional RaygunSettings
    onBeforeSend     = callbackClosure,        // optional closure to inspect/mutate/cancel payloads
    ignoreExceptions = ["MissingInclude"]      // optional array of exception types to skip
);

send()

Sends an error report to Raygun synchronously and returns the cfhttp result struct.

result = raygun.send(
    issueData      = cfcatchOrException,          // required - cfcatch/exception struct
    userCustomData = raygunUserCustomDataInstance, // optional
    tags           = ["tag1", "tag2"],             // optional array of strings
    user           = raygunIdentifierMessage,      // optional RaygunIdentifierMessage
    groupingKey    = "my-custom-grouping-key",     // optional string
    sendAsync      = false                         // optional, default false
);

The issueData argument accepts cfcatch or exception structs. These structs are expected to contain fields like message, type, stacktrace, and tagcontext.

sendAsync()

Convenience wrapper that calls send() with sendAsync=true. Returns void. Failures are logged to the Raygun4CFML log file.

raygun.sendAsync(
    issueData      = cfcatchOrException,
    userCustomData = customData,
    tags           = ["async", "background"],
    user           = userIdentifier,
    groupingKey    = "my-grouping-key"
);

Record a trail of events leading up to an error. Breadcrumbs are automatically included in subsequent send()/sendAsync() calls.

raygun = new com.raygun.RaygunClient(apiKey = "YOUR_API_KEY");

// Record breadcrumbs as your application executes
raygun.recordBreadcrumb(message = "User logged in");
raygun.recordBreadcrumb(
    message    = "Query executed",
    level      = "debug",        // debug, info, warning, error (default: info)
    category   = "database",
    className  = "UserDAO",
    methodName = "findById",
    lineNumber = 42,
    customData = {"sql": "SELECT * FROM users WHERE id = ?"}
);
raygun.recordBreadcrumb(message = "Page rendered", level = "info");

// Breadcrumbs are included when an error is sent
try {
    // application code
} catch (any e) {
    raygun.send(e);
}

// Clear breadcrumbs after sending if needed
raygun.clearBreadcrumbs();

The recordBreadcrumb() method returns this for chaining:

raygun
    .recordBreadcrumb(message = "Step 1")
    .recordBreadcrumb(message = "Step 2")
    .recordBreadcrumb(message = "Step 3");

onBeforeSend Hook

Register a callback to inspect, mutate, or cancel payloads before they are sent to Raygun.

// Cancel sending for specific error types
raygun = new com.raygun.RaygunClient(
    apiKey = "YOUR_API_KEY",
    onBeforeSend = function(payload) {
        // Return false to cancel sending
        if (payload.details.error.className == "AbortException") {
            return false;
        }
        // Return the (optionally modified) payload to proceed
        return payload;
    }
);

The callback receives the full deserialized payload struct. Return false to cancel, return a struct to send the (optionally modified) payload, or throw an exception to proceed with the original payload.

You can also set the callback after construction:

raygun.setOnBeforeSend(function(payload) {
    payload.details.tags.append("extra-tag");
    return payload;
});

Ignore Exceptions

Skip sending specific exception types entirely:

raygun = new com.raygun.RaygunClient(
    apiKey           = "YOUR_API_KEY",
    ignoreExceptions = ["MissingInclude", "AbortException", "LockTimeout"]
);

Matching is case-insensitive. Ignored exceptions cause send() to return an empty string without building or transmitting the payload. You can update the list at any time via setIgnoreExceptions().


RaygunSettings

Controls client behavior including raw data capture, HTTP status codes, API endpoint, timeout, and retry settings.

settings = new com.raygun.environment.RaygunSettings(
    rawDataMaxLength = 10000,                              // default: 4096
    statusCode       = 418,                                // default: 500
    apiEndpoint      = "https://custom.example.com/entries", // default: Raygun API
    httpTimeout      = 30,                                 // default: 10 (seconds)
    maxRetries       = 3,                                  // default: 2
    retryDelay       = 2                                   // default: 1 (seconds)
);

raygun = new com.raygun.RaygunClient(
    apiKey   = "YOUR_API_KEY",
    settings = settings
);
Setting Type Default Description
rawDataMaxLength numeric4096 Maximum characters of raw request body to capture
statusCode numeric500 Default HTTP status code (auto-overridden to 404 for MissingInclude)
apiEndpoint stringhttps://api.raygun.com/entries Raygun API endpoint URL
httpTimeout numeric10 HTTP request timeout in seconds
maxRetries numeric2 Maximum retry attempts after initial failure (0 to disable)
retryDelay numeric1 Delay in seconds between retry attempts

RaygunContentFilter

Protects sensitive data from being sent to Raygun. Accepts an array of filter rules, each with a filter (field name or glob pattern to match) and a replacement (value to substitute). Filters are applied against both top-level payload keys and JSON content inside rawData.

Exact match:

contentFilter = new com.raygun.filter.RaygunContentFilter([
    {filter: "password", replacement: "[FILTERED]"},
    {filter: "creditCard", replacement: "[FILTERED]"}
]);

Wildcard patterns (using * as a glob):

contentFilter = new com.raygun.filter.RaygunContentFilter([
    {filter: "pass*", replacement: "[FILTERED]"},      // matches password, passphrase, passCode
    {filter: "*token", replacement: "[FILTERED]"},     // matches authToken, refreshToken
    {filter: "*secret*", replacement: "[FILTERED]"}    // matches mySecretKey, topSecret123
]);

Wildcard matching is case-insensitive and works on nested structs and rawData JSON.

raygun = new com.raygun.RaygunClient(
    apiKey        = "YOUR_API_KEY",
    contentFilter = contentFilter
);

RaygunUserCustomData

Attach arbitrary diagnostic data to error reports. This data appears in Raygun's Custom Data tab.

Using the constructor:

customData = new com.raygun.user.RaygunUserCustomData(
    userCustomData = {
        "session": {"memberID": "12345", "plan": "pro"},
        "params": {"currentAction": "checkout"}
    }
);

Using the builder pattern:

customData = new com.raygun.user.RaygunUserCustomData();
customData.add("sessionID", "abc-123");
customData.add("lastAction", "checkout");
customData.add("cartItems", 3);

RaygunIdentifierMessage

Track affected users. All fields are optional.

Field Type Description
identifier stringUnique user identifier (e.g. email, user ID)
isAnonymous booleanWhether the user is anonymous (default: true)
email stringUser's email address
fullName stringUser's full name
firstName stringUser's first name
uuid stringUnique identifier / session ID

Using the builder pattern (recommended):

user = new com.raygun.message.RaygunIdentifierMessage()
    .setIdentifier("[email protected]")
    .setIsAnonymous(false)
    .setEmail("[email protected]")
    .setFullName("Jane Smith")
    .setFirstName("Jane")
    .setUuid("550e8400-e29b-41d4-a716-446655440000");

Using the constructor:

user = new com.raygun.message.RaygunIdentifierMessage(
    identifier  = "[email protected]",
    isAnonymous = false,
    email       = "[email protected]",
    fullName    = "Jane Smith",
    firstName   = "Jane",
    uuid        = "550e8400-e29b-41d4-a716-446655440000"
);

Full Example (Application.cfc onError)

component {

    this.name = "MyApp";

    public void function onError(required any exception, required string eventName) {

        // Custom diagnostic data
        var customData = new com.raygun.user.RaygunUserCustomData();
        customData.add("sessionID", session.sessionID);
        customData.add("currentAction", cgi.SCRIPT_NAME);

        // Tags for filtering in the Raygun dashboard
        var tags = ["onError", "production", "unhandled exception"];

        // User identification
        var user = new com.raygun.message.RaygunIdentifierMessage()
            .setIdentifier(session.userEmail)
            .setIsAnonymous(false)
            .setFullName(session.userFullName);

        // Content filtering with wildcards to protect sensitive data
        var contentFilter = new com.raygun.filter.RaygunContentFilter([
            {filter: "pass*", replacement: "[FILTERED]"},
            {filter: "*token", replacement: "[FILTERED]"},
            {filter: "creditCard", replacement: "[FILTERED]"},
            {filter: "ssn", replacement: "[FILTERED]"}
        ]);

        // Custom settings with retry and timeout
        var settings = new com.raygun.environment.RaygunSettings(
            rawDataMaxLength = 10000,
            httpTimeout      = 15,
            maxRetries       = 3
        );

        // Initialize with hooks and ignore list
        var raygun = new com.raygun.RaygunClient(
            apiKey           = "YOUR_API_KEY",
            appVersion       = "1.0.0",
            contentFilter    = contentFilter,
            settings         = settings,
            ignoreExceptions = ["AbortException"]
        );

        // Record breadcrumbs for context
        raygun.recordBreadcrumb(message = "Error handler triggered", level = "error");

        raygun.send(
            issueData      = arguments.exception,
            userCustomData = customData,
            tags           = tags,
            user           = user
        );
    }

}

Automatically Captured Data

The following data is captured automatically with every error report — no additional configuration required.

Request:

  • URL (host, script name, path info)
  • HTTP method
  • Query string
  • Request headers
  • CGI scope
  • Form data (FORM scope, values truncated to 256 characters)
  • URL parameters (URL scope)
  • Client IP address
  • Raw request body (truncated to rawDataMaxLength, default 4096 characters; only for non-GET requests with non-form content types)

Environment:

  • Operating system name and version
  • System architecture
  • JVM vendor, version, and runtime name
  • Heap memory (available and total)
  • Physical memory (available and total, where accessible)
  • CFML engine and version (e.g. "Lucee 6.1.0.243", "BoxLang 1.0.0")
  • Processor count
  • System locale
  • UTC offset (hours)

Response:

  • HTTP status code (default 500, configurable via RaygunSettings)
  • HTTP status description
  • Automatic 404 status for MissingInclude exceptions

Error:

  • Error message and type
  • Stack trace (parsed from Java stack trace string)
  • Tag context (CFML file/line references, with code snippets on Lucee and BoxLang)
  • Error code and extended info (where available)
  • Nested/chained exceptions (via cause field)
  • Database error details: SQL, query error, native error code, SQL state (for database type exceptions)

Payload Safety:

  • Total payload automatically capped at 128KB
  • Oversized payloads are reduced by progressively stripping expendable fields

Samples

The /samples directory contains working examples for common integration patterns:

Directory Description
samples/try-catch/ Simple try/catch error reporting in a standalone script
samples/app-cfc-no-filter/ Application.cfc global error handler with user data, tags, and user identification
samples/app-cfc-content-filter/ Application.cfc with content filtering to protect sensitive fields
samples/app-cfc-settings/ Application.cfc with custom RaygunSettings (raw data length, status code)
samples/datasources-and-sql/ Database error reporting with SQL exception details

Configuring the API Key for Samples

The samples load the Raygun API key automatically — no need to edit each file. The key is resolved in this order:

  1. Local config file — samples/.env.json (recommended for local development)
  2. Environment variable — RAYGUN_API_KEY
  3. Placeholder — falls back to <YOUR API KEY> if neither is set

Option 1: Local config file

Copy the template and add your key:

cp samples/.env.json.sample samples/.env.json

Then edit samples/.env.json:

{
    "RAYGUN_API_KEY": "your-api-key-here"
}

This file is gitignored and will not be committed.

Option 2: Environment variable

export RAYGUN_API_KEY="your-api-key-here"

Or pass it when starting a CommandBox server:

RAYGUN_API_KEY="your-api-key-here" box server start serverConfigFile=server-lucee-6-1.json

Development & Testing

Dependencies

  • TestBox (dev dependency, installed via CommandBox)

Setup

box install

Formatting

box run-script format          # format all source files
box run-script format:check    # check formatting without modifying files

Running Tests

./run-tests.sh server-lucee-6-1.json    # single engine
./run-tests.sh                           # all 20 engines sequentially
box run-script test                      # shortcut: Lucee 6.1
box run-script test:all                  # shortcut: all engines

Available Test Servers

Server Config Engine Port
server-lucee-8-0.json Lucee 8.0 Alpha9202
server-lucee-5-3.json Lucee 5.39196
server-lucee-5-4.json Lucee 5.49191
server-lucee-6-0.json Lucee 6.09194
server-lucee-6-1.json Lucee 6.19195
server-lucee-6-2.json Lucee 6.29199
server-lucee-7-0.json Lucee 7.09200
server-lucee-7-1.json Lucee 7.19201
server-lucee-light-5-3.json Lucee Light 5.39203
server-lucee-light-5-4.json Lucee Light 5.49204
server-lucee-light-6-0.json Lucee Light 6.09205
server-lucee-light-6-1.json Lucee Light 6.19206
server-lucee-light-6-2.json Lucee Light 6.29207
server-lucee-light-7-0.json Lucee Light 7.09208
server-lucee-light-7-1.json Lucee Light 7.19209
server-lucee-light-8-0.json Lucee Light 8.0 Alpha9210
server-adobe-2021.json Adobe ColdFusion 20219192
server-adobe-2023.json Adobe ColdFusion 20239193
server-adobe-2025.json Adobe ColdFusion 20259198
server-boxlang-1.json BoxLang 19197

Version History

For detailed version history, refer to the CHANGELOG.md.

Contribution Guidelines

Raygun4CFML is not an official Raygun library and is not maintained by Raygun staff.

Contributions are welcome! Here's how:

  1. Fork the main repository at https://github.com/MindscapeHQ/raygun4cfml
  2. Create a feature branch for your changes
  3. Run the formatter before submitting: box run-script format
  4. Add or update tests for any behavior changes
  5. Update README.md and CHANGELOG.md for public API changes
  6. Submit a pull request

Coordination via X (@AgentK) or GitHub (@TheRealAgentK) is encouraged before starting any work.

For more active development, visit the development fork at https://github.com/TheRealAgentK/raygun4cfml.

License

Apache 2.0

Install this module and follow the guidelines in README.md as well as in /samples.

History and Plan

3.0.0 (July 21, 2026)

New Features:

  • Breadcrumbs: Record a trail of events leading up to an error via recordBreadcrumb() and clearBreadcrumbs(). Breadcrumbs are automatically included in subsequent send()/sendAsync() calls with timestamp, level, type, category, message, className, methodName, lineNumber, and customData fields (#46).
  • onBeforeSend hook: Register a callback via constructor or setOnBeforeSend() to inspect, mutate, or cancel payloads before sending. Return false to cancel, return a modified struct to mutate, or throw to proceed with the original payload.
  • Ignore exceptions list: Skip specific exception types via ignoreExceptions constructor argument or setIgnoreExceptions(). Case-insensitive matching (e.g. ["MissingInclude", "AbortException"]).
  • Wildcard content filter keys: RaygunContentFilter now supports glob-style * wildcards in filter patterns (e.g. "pass*" matches password, passphrase, passCode). Exact-match filters continue to work as before.
  • Payload size enforcement: Payloads exceeding 128KB are automatically reduced by progressively stripping expendable fields (rawData, userCustomData, CGI data, headers, form). Form field values are truncated to 256 characters.
  • Configurable API endpoint: Set a custom Raygun API endpoint via RaygunSettings.apiEndpoint (default: https://api.raygun.com/entries).
  • HTTP timeout: All API requests now have a configurable timeout via RaygunSettings.httpTimeout (default: 10 seconds).
  • Automatic retry: Failed HTTP requests are retried with configurable RaygunSettings.maxRetries (default: 2) and RaygunSettings.retryDelay (default: 1 second). Set maxRetries=0 to disable.
  • Additional environment fields: processorCount, locale, and utcOffset are now captured in every error report.
  • Sample API key configuration: Samples now load the Raygun API key automatically from samples/.env.json (gitignored) or the RAYGUN_API_KEY environment variable — no more manual copy-paste into each file.

Bug Fixes:

  • Fixed settings not propagating to RaygunRequestMessage and RaygunResponseMessage
  • Fixed empty stackTrace when Java stack trace is empty but CFML tag context is available
  • Fixed case-sensitive exception type checks (e.g. "database" vs "Database")
  • Fixed unsafe CGI scope access in RaygunRequestMessage and RaygunMessageDetails
  • Fixed sync/async HTTP error handling inconsistency in RaygunClient
  • Fixed thread name typo in async sending
  • Fixed typed property defaults (default="" on non-string typed properties)
  • Added isNull() guards on getSettings()/getContentFilter() to prevent NPE on strict engines
  • Fixed RaygunContentFilter initialization on Adobe ColdFusion 2025 Update 11, where the engine's built-in setFilter() function collided with the generated property setter

Code Quality:

  • Centralized all magic strings and constants in RaygunConfig (API endpoint, log file name, content types, HTTP methods, size limits, timeout/retry defaults)
  • Replaced isClosure() with isCustomFunction() for cross-engine compatibility
  • 174 test specs (up from 70), covering all components
  • Expanded the test matrix to 20 engines, including Lucee 8 Alpha and matching Lucee Light configurations for every supported Lucee line

2.1.0 (Jan 21 2025)

  • Add support for response.statusCode and statusDescription (#48)

2.0.1 (Jan 13 2025)

  • Fixed issue with ACF Content filtering and CGI-Scope

2.0.0 (Jan 12 2025)

  • Fixed issue with Boxlang CGI-Scope
  • Added Lucee 5.3 support back-in and provided test server setup

2.0.0-alpha (January 4 2025)

  • Complete re-write, breaking API changes and changes in essential functionality:
    • Raygun4CFML is now entirely written in CFML script.
    • The original stack trace is now tracked in the stack trace field, not the CFML TagContext. The latter is now in the exception's data section, where available.
    • There is now proper support for nested exceptions (based on existence of cause field).
    • Content filtering (RaygunContentFilter), user identifier (RaygunIdentifierMessage) and user custom data (RaygunUserCustomData) are now using the builder-pattern approach to be setup for RaygunClient.
    • Size of raw data can be configured (#42).
    • SQL exception tracking has been improved (#44).
    • Constants are now tracked in their own static component and some can be overwritten by RaygunSettings.
    • ProductCheck and RaygunInternalTools are now static components.
    • CFML engine is being track in Raygun's Environment tab now.
  • Samples in /samples have been reworked.
  • Unit/Integration tests are in /tests/specs.
  • Code formatting via run-script format was added for Commandbox.
  • Project contains custom CFML server declarations for testing on ports (port 9191 upwards).
  • All files have improved code documentation.
  • Engine-specific changes:
    • Support for Adobe ColdFusion before ACF 2021 has been stopped. ACF 2018 and earlier are - as CFML engines go - not supported any more, please upgrade your platforms.
    • Support for any versions of Railo has been stopped. Lucee support is set to Lucee 5.4 and newer, but this might be extended to 5.3 in a future 2.0.0 pre-release.
    • Support for Boxlang 1.0.0 has been added.

1.7.0 (November 14 2024)

  • Fixes issues around non-existent HTTP request objects when run on ACF and in a thread context
  • Fixes access to JVM memory beans depending on JVM settings and JVM type available
  • Minimum requirements are now Lucee 5+ anmd ACF 2018+
  • Fixes issue with content filter not trailing deep into payload

1.6.0 (November 23 2023)

  • Fixed issue in RaygunExceptionMessage on recent version of ACF
  • Changed content/sensitivity filter behaviour. It now runs just before data is being send to RG and filters against the full pre-send payload and not just URL/FORM scopes.

1.5.0 (November 14 2022)

  • Added .sendAsync() entry point wrapping the HTTP call into its own thread.
  • Regorganisation of code in RaygunClient
  • Improving handling of getHTTPRequestData in RaygunRequestMessage
  • Changed HTTP endpoint to .com
  • Supports groupingKey now

1.4.0 (May 24 2022)

  • Supporting stack traces where certain elements (like TagContext) don't exist
  • Support for specifc Java strack traces stemming from asynchronous handling

1.3.1 (Jul 26 2021)

  • Physical memory tracking again under certain conditions, provided the underlying Java code is available on the JVM (modules opened up).

1.3.0 (Jul 21 2021)

  • Raygun4CFML is now tracking heap memory in the availableVirtualMemory and availableFreeMemory fields and not physical memory anymore. Fixed accessibility issues of internal classes post-Java 8 and the library should now be working fine across all JDKs.

1.2.1 (Jun 16 2021)

  • Minor changes to stacktrace handling
  • Additional of Path Info to Request URL data

1.2.0 (Jun 8 2021)

  • Support for version (#33)
  • Fixed stack traces to work better with Lucee and ACF 2021

1.1.0 (Jan 2 2016)

  • Refactored packages and file/dir locations to cater for ideas in PR28 and to prepare for Forgebox packaging
  • Added Forgebox packaging
  • Enhanced documentation
  • Changed internal code to make the CFCs independen of package paths
  • Changed internal code to instantiate CFCs using "new", therefore breaking compatibility with ACF8 (and probably Railo 3)
  • From this version onwards, raygun4cfml will use semantic versioning for the version numbers (semver.org)

1.0.2.0 (Nov 14 2015):

  • Merged PR26 and modified/refactored it slightly

1.0.1.0 (Nov 14 2015):

  • Merged PR23 and PR24 and modified/refactored them slightly

1.0.0.1 (Jul 1 2015):

  • Merged PR21 from Alex --- fixing naming inconsistencies in the user tracking object

1.0.0.0 (Jan 3 2015):

  • Support for Tags and Affected User (please check samples 4 and 5 in samples/global_errorhandler/errortemplate.cfm and the code in the tests_manual directories for samples on how to use them)
  • Moves statusCode from request to details structure
  • Changed the behaviour of userCustomData. Essentially removed all the old, backwards compatibility code that came in from PR15/16 (in 0.5.0.0) --- this change had lead to much cleaner and simpler code. Note: This change will break backwards compatibility for people who have used customRequestData before (please check sample 3 samples/global_errorhandler/errortemplate.cfm)

0.5.0.0 (Dec 31 2014): merged and edited PR/ISSUE 15/16 and fixed a CF 9 issue. Please be aware that samples have changed due to a new way of passing in custom data.

0.4.0.0alpha (Jan 10 2014): Various small fixes, merged and edited PR10

0.3.4.0alpha (May 1 2013): Various bugfixes and improvements, fix for queryString, machineName is now server's IP Address and more

0.3.0.0alpha (Apr 10 2013): Switched Stracktrace with TagContext data to make it more relevant for Dashboard display of CFML errors, implemented support for the session and param structures within request, updated sample files to reflect the changes

0.2.2.0alpha (Mar 29 2013): Various fixes, better support for cfcatch (Expression) vs error structs

0.2.1.1alpha (Mar 28 2013): Merged PR from possum888, added sample for using RG in a global errorhandler or via cferror

0.2.1.0alpha (Mar 22 2013): Added support for POST rawData, CFML Form-Scope and implemented a scope-based content filtering allowing to replace sensitive scope data before it is being sent to Raygun.io

0.1.0.0alpha (Feb 15 2013): Initial Release, tested on ACF 9.

$ box install raygun4cfml

No collaborators yet.
     
5.00 / 1
  • {{ getFullDate("2016-01-01T20:46:31Z") }}
  • {{ getFullDate("2026-07-21T06:26:45Z") }}
  • 7,920
  • 6,003